﻿// Copyright (C) 2009 - 2013 EvTech Project
//
// This program is free software: you can redistribute it and/or modify
// it under the +terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/. 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Media;

namespace Piirto
{
    public partial class RedrawGameForm : Form
    {
        static Color VISIBLEROUTECOLOR = Color.White;
        static Color VISITEDROUTECOLOR = Color.Gray;
        static Color UNVISITEDROUTECOLOR = Color.Brown;
        static Color BGCOLOR = Color.Black;

        static bool DEBUGMODE = false;

        private PiirtoMainForm piirtoMainForm;
        private enum MODE { DRAW, REDRAW, GAMEOVER };
        private MODE currentMode;
        private RouteTool currentPath;
        private Bitmap currentImage;
        private Character playerCharacter;
        private Dictionary<Image, Character> characters;
        private SoundPlayer winsound;
        private Rectangle startArea, goalArea;
        private int level;
        private int maxlevel;
        private int penWidth;
        private bool isSoundOn;
        private SelectCharacterForm selectSpriteDlg;

        public RedrawGameForm()
        {
            InitializeComponent();
        }

        public RedrawGameForm(PiirtoMainForm piirtoMainForm)
        {
            InitializeComponent();
            currentMode = MODE.DRAW;
            lblInstructions.Text = "Piirrä reitti";
            this.piirtoMainForm = piirtoMainForm;
            level = 1;
            maxlevel = 3;
            penWidth = 80;
            isSoundOn = true;
            lblSize.Text = penWidth.ToString();
            characters = new Dictionary<Image, Character>();
            startArea = new Rectangle();
            goalArea = new Rectangle();
            currentImage = new Bitmap(800, 800 * paintImage.Height / paintImage.Width);
            Graphics g = Graphics.FromImage(currentImage);
            g.Clear(BGCOLOR);
            g.Dispose();
            paintImage.Image = new Bitmap(currentImage);
            currentPath = new RouteTool(VISIBLEROUTECOLOR, VISITEDROUTECOLOR, UNVISITEDROUTECOLOR, 5, 5);
            selectSpriteDlg = new SelectCharacterForm();
            try
            {
                playerCharacter = selectSpriteDlg.getSelected();

                this.winsound = new SoundPlayer();
                this.winsound.SoundLocation = "./sounds/default_win.wav";
                this.winsound.LoadAsync();
            }
            catch (Exception ex)
            {
                Console.Out.WriteLine(ex.Message);
            }
        }

        private void RedrawGameForm_Load(object sender, EventArgs e)
        {

            resizeText(lblInstructions);
            resizeText(lblSize);
            setPenWidth(((int)(playerCharacter.getSprite().getLength() + 1) / 10 + maxlevel) * 10);
        }

        private void paintImage_MouseDown(object sender, MouseEventArgs e)
        {
            int x, y;

            if (paintImage.Image.Width / paintImage.Image.Height > paintImage.Width / paintImage.Height)
            {
                x = e.X * paintImage.Image.Width / paintImage.Width;
                y = (e.Y - (paintImage.Height - paintImage.Image.Height * paintImage.Width /
                    paintImage.Image.Width) / 2) * paintImage.Image.Width / paintImage.Width;
            }
            else
            {
                x = (e.X - (paintImage.Width - paintImage.Image.Width * paintImage.Height /
                    paintImage.Image.Height) / 2) * paintImage.Image.Height / paintImage.Height;
                y = e.Y * paintImage.Image.Height / paintImage.Height;
            }

            if (currentMode == MODE.REDRAW)
            {
                if (playerCharacter.getSprite().testCollision(x, y))
                {
                    playerCharacter.getSprite().Snapped = true;
                    playerCharacter.getSprite().startDragging(x, y);
                    currentPath.visitPoint(x, y);
                }
            }
            else if(currentMode == MODE.DRAW)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    currentPath.newPoint(x, y);
                }
                draw();
            }
        }

        private void paintImage_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                int x, y;

                if (paintImage.Image.Width / paintImage.Image.Height > paintImage.Width / paintImage.Height)
                {
                    x = e.X * paintImage.Image.Width / paintImage.Width;
                    y = (e.Y - (paintImage.Height - paintImage.Image.Height * paintImage.Width /
                        paintImage.Image.Width) / 2) * paintImage.Image.Width / paintImage.Width;
                }
                else
                {
                    x = (e.X - (paintImage.Width - paintImage.Image.Width * paintImage.Height /
                        paintImage.Image.Height) / 2) * paintImage.Image.Height / paintImage.Height;
                    y = e.Y * paintImage.Image.Height / paintImage.Height;
                }

                if (currentMode == MODE.REDRAW)
                {
                    if (playerCharacter.getSprite().Snapped)
                    {
                        Bitmap img = new Bitmap(this.paintImage.Image.Width, this.paintImage.Image.Height);
                        Graphics g = Graphics.FromImage(img);
                        g.Clear(BGCOLOR);
                        if (currentPath.getRoutePoints().Count > 0)
                        {
                            currentPath.Paint(g);
                        }
                        g.Dispose();

                        if (playerCharacter.getSprite().testCollision(goalArea)
                            && currentPath.isAllPointsVisited())
                        {
                            playerCharacter.getSprite().Snapped = false;
                            playerCharacter.getMovesound().Stop();
                            if (level < maxlevel)
                            {
                                if (isSoundOn)
                                {
                                    playerCharacter.getWinsound().PlaySync();
                                }
                                setLevel(level + 1);
                                currentPath.resetVisitedPoints();
                                if (isSoundOn)
                                {
                                    playerCharacter.getMovesound().PlayLooping();
                                }
                                lblInstructions.Text = "Seuraa reittiä " + level + "/" + maxlevel;
                            }
                            else
                            {
                                if (isSoundOn)
                                {
                                    this.winsound.Play();
                                }
                                lblInstructions.Text = "Voitit!";
                                currentMode = MODE.GAMEOVER;
                            }
                        }
                        else if (playerCharacter.getSprite().testCollision(img, currentPath.getRouteColor()))
                        {
                            // start again
                            playerCharacter.getSprite().Snapped = false;
                            if (isSoundOn)
                            {
                                playerCharacter.getCrashsound().PlaySync();
                            }
                            playerCharacter.getSprite().setLocation(currentPath.getCurrentPoint());
                            playerCharacter.getSprite().setAngle(0);
                            currentPath.visitPoint(playerCharacter.getSprite().getLocation().X,
                                playerCharacter.getSprite().getLocation().Y);
                            //currentPath.resetVisitedPoints();
                            if (isSoundOn)
                            {
                                playerCharacter.getMovesound().PlayLooping();
                            }
                        }
                        else
                        {
                            currentPath.visitPoint(x, y);
                            playerCharacter.getSprite().drag(x, y);
                        }
                        img.Dispose();
                    }
                    else if (playerCharacter.getSprite().testCollision(x, y))
                    {
                        playerCharacter.getSprite().Snapped = true;
                        playerCharacter.getSprite().startDragging(x, y);
                        currentPath.visitPoint(x, y);
                    }
                }
                else if (currentMode == MODE.DRAW)
                {
                    if (currentPath == null)
                    {
                        return;
                    }
                    currentPath.newPoint(x, y);
                }
            }
            draw();
        }

        private void paintImage_MouseUp(object sender, MouseEventArgs e)
        {
            if (currentMode == MODE.REDRAW)
            {
                playerCharacter.getSprite().Snapped = false;
                if (playerCharacter.getSprite().testCollision(goalArea)
                    && currentPath.isAllPointsVisited())
                {
                    playerCharacter.getMovesound().Stop();
                    if (level < maxlevel)
                    {
                        playerCharacter.getWinsound().PlaySync();
                        setLevel(level + 1);
                        currentPath.resetVisitedPoints();
                        if (isSoundOn)
                        {
                            playerCharacter.getMovesound().PlayLooping();
                        }
                        lblInstructions.Text = "Seuraa reittiä " + level + "/" + maxlevel;
                    }
                    else
                    {
                        this.winsound.Play();
                        lblInstructions.Text = "Voitit!";
                        currentMode = MODE.GAMEOVER;
                    }
                }
            }
            else if (currentMode == MODE.DRAW && currentPath.getRoutePoints().Count > 0)
            {
                setLevel(1);
                if (testLevels())
                {
                    currentPath.packRoute();
                    playerCharacter.getSprite().Visible = true;
                    currentPath.resetVisitedPoints();
                    currentMode = MODE.REDRAW;
                    if (isSoundOn)
                    {
                        playerCharacter.getMovesound().PlayLooping();
                    }
                    lblInstructions.Text = "Seuraa reittiä " + level + "/" + maxlevel;
                }
                else
                {
                    restart();
                }
            }
        }

        private bool testLevels()
        {
            return currentPath.testRoute();
        }

        private void makePath()
        {
            Graphics g = Graphics.FromImage(currentImage);
            g.Clear(BGCOLOR);
            if (currentPath.getRoutePoints().Count > 0)
            {
                int size = currentPath.getPenWidth();
                currentPath.Paint(g);
                g.FillEllipse(Brushes.White, currentPath.getRoutePoints()[0].X - (size / 2),
                    currentPath.getRoutePoints()[0].Y - (size / 2), size, size);
                g.FillEllipse(Brushes.White, currentPath.getRoutePoints()[currentPath.getRoutePoints().Count - 1].X - (size / 2),
                    currentPath.getRoutePoints()[currentPath.getRoutePoints().Count - 1].Y - (size / 2), size, size);
                g.Dispose();
                startArea = new Rectangle(currentPath.getRoutePoints()[0].X - (size / 2),
                    currentPath.getRoutePoints()[0].Y - (size / 2), size, size);
                goalArea = new Rectangle(currentPath.getRoutePoints()[currentPath.getRoutePoints().Count - 1].X - (size / 2),
                    currentPath.getRoutePoints()[currentPath.getRoutePoints().Count - 1].Y - (size / 2), size, size);
            }
            else
            {
                startArea = new Rectangle();
                goalArea = new Rectangle();
            }
        }

        private void draw()
        {
            Graphics g = Graphics.FromImage(this.paintImage.Image);
            g.Clear(BGCOLOR);
            if (currentPath.getRoutePoints().Count > 0)
            {
                currentPath.Paint(g);
            }
            if (currentMode != MODE.DRAW)
            {
                g.FillEllipse(Brushes.SpringGreen, startArea.X, startArea.Y, startArea.Width, startArea.Height);
                g.FillEllipse(Brushes.IndianRed, goalArea.X, goalArea.Y, goalArea.Width, goalArea.Height);

                if (DEBUGMODE)
                {
                    for (int i = 0; i < currentPath.getRoutePoints().Count; i++)
                    {
                        g.FillEllipse(Brushes.Red, currentPath.getRoutePoints()[i].X - 3,
                            currentPath.getRoutePoints()[i].Y - 3, 6, 6);
                        if (i > 0)
                        {
                            g.DrawLine(Pens.Red, currentPath.getRoutePoints()[i - 1], currentPath.getRoutePoints()[i]);
                        }
                    }
                    g.FillEllipse(Brushes.Black, currentPath.getCurrentPoint().X - 4,
                        currentPath.getCurrentPoint().Y - 4, 8, 8);
                    if (currentPath.getCurrentPointIndex() + 1 < currentPath.getRoutePoints().Count)
                    {
                        g.DrawLine(Pens.Black, currentPath.getCurrentPoint(),
                            currentPath.getRoutePoints()[currentPath.getCurrentPointIndex() + 1]);
                    }
                }
            }
            else if (currentPath.getRoutePoints().Count > 0)
            {
                int size = currentPath.getPenWidth();
                g.FillEllipse(Brushes.SpringGreen, currentPath.getRoutePoints()[0].X - (size / 2),
                    currentPath.getRoutePoints()[0].Y - (size / 2), size, size);
                g.FillEllipse(new HatchBrush(HatchStyle.Percent50, Color.IndianRed, Color.Transparent),
                    currentPath.getRoutePoints()[currentPath.getRoutePoints().Count - 1].X - (size / 2),
                    currentPath.getRoutePoints()[currentPath.getRoutePoints().Count - 1].Y - (size / 2), size, size);
                
            }
            playerCharacter.getSprite().Paint(g);

            //just for testing
            //g.DrawString("Angle " + playerCharacter.getSprite().getAngle(), DefaultFont, Brushes.Black, 0, 0);
            g.Dispose();
            this.paintImage.Refresh();
        }


        private void btnBack_Click(object sender, EventArgs e)
        {
            closeForm();
        }

        private void RedrawGameForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true;
            closeForm();
        }

        private void closeForm()
        {
            restart();
            this.Hide();
            this.piirtoMainForm.Show();
        }

        private void lblInstructions_TextChanged(object sender, EventArgs e)
        {
            resizeText(lblInstructions);
        }


        private void resizeText(Label lbl)
        {
            float fontSize = lbl.Font.Size;
            float textSize = 1;
            lbl.UseCompatibleTextRendering = true;
            Graphics gr = lbl.CreateGraphics();
            textSize = gr.MeasureString(lbl.Text, lbl.Font).Width;
            if (textSize == 0)
            {
                return;
            }
            fontSize = fontSize * lbl.ClientRectangle.Width / textSize;
            lbl.Font = new Font(lbl.Font.FontFamily, fontSize);

            textSize = gr.MeasureString(lbl.Text, lbl.Font).Height;
            if (textSize > lbl.ClientRectangle.Height)
            {
                fontSize = fontSize * lbl.ClientRectangle.Height / textSize;
                lbl.Font = new Font(lbl.Font.FontFamily, fontSize);
            }
            gr.Dispose();
            lbl.Refresh();
        }

        private void btnNew_Click(object sender, EventArgs e)
        {
            restart();
        }

        private void restart()
        {
            Graphics g = Graphics.FromImage(currentImage);
            g.Clear(BGCOLOR);
            g.Dispose();
            g = Graphics.FromImage(paintImage.Image);
            g.Clear(BGCOLOR);
            g.Dispose();
            currentPath.clear();
            currentMode = MODE.DRAW;
            playerCharacter.getSprite().Visible = false;
            playerCharacter.getMovesound().Stop();
            lblInstructions.Text = "Piirrä reitti";
            draw();
        }

        private void btnUp_Click(object sender, EventArgs e)
        {
            setPenWidth(penWidth + 5);
        }

        private void btnDown_Click(object sender, EventArgs e)
        {
            setPenWidth(penWidth - 5);
        }

        private void setPenWidth(int width)
        {
            if (width > playerCharacter.getSprite().getLength() + (20 * maxlevel))
                penWidth = (int)(playerCharacter.getSprite().getLength() + (20 * maxlevel));
            else if (width - (5 * maxlevel) < playerCharacter.getSprite().getLength())
                penWidth = (int)(playerCharacter.getSprite().getLength() + (5 * maxlevel));
            else
                penWidth = width;

            lblSize.Text = penWidth.ToString();
            resizeText(lblSize);
            currentPath.setPenWidth(penWidth - level * 5);
            currentPath.setBorderWidth((int)playerCharacter.getSprite().getLength());
            makePath();
            draw();
        }

        private void setLevel(int level)
        {
            this.level = level;
            currentPath.setPenWidth(penWidth - level * 5);
            currentPath.setBorderWidth((int)playerCharacter.getSprite().getLength());
            makePath();
            playerCharacter.getSprite().setLocation(
                new Point(startArea.X + startArea.Width / 2,
                    startArea.Y + startArea.Height / 2));
            playerCharacter.getSprite().setAngle(0);
            draw();
        }
        /*
        private void btnSprite_Click(object sender, EventArgs e)
        {
            if(sender is Button)
            {
                if (characters.ContainsKey(((Button)sender).BackgroundImage))
                {
                    Character oldCharacter = playerCharacter;
                    playerCharacter = characters[((Button)sender).BackgroundImage];
                    if (playerCharacter != oldCharacter)
                    {
                        playerCharacter.getSprite().setLocation(oldCharacter.getSprite().getLocation());
                        playerCharacter.getSprite().setAngle(oldCharacter.getSprite().getAngle());
                        playerCharacter.getSprite().Visible = oldCharacter.getSprite().Visible;
                        playerCharacter.getSprite().Snapped = false;

                        oldCharacter.getSprite().Visible = false;
                        oldCharacter.getSprite().Snapped = false;
                        oldCharacter.getMovesound().Stop();
                        oldCharacter.getCrashsound().Stop();
                        oldCharacter.getWinsound().Stop();

                        if (!isSoundOn)
                        {
                            playerCharacter.getMovesound().Stop();
                        }
                        else if (currentMode == MODE.REDRAW)
                        {
                            playerCharacter.getMovesound().PlayLooping();
                        }

                        draw();
                    }
                }
            }
        }
         * */

        private void btnToggleSound_Click(object sender, EventArgs e)
        {
            toggleSound();
        }

        private void toggleSound()
        {
            isSoundOn = !isSoundOn;
            if (!isSoundOn)
            {
                btnToggleSound.BackgroundImage = global::Piirto.Properties.Resources.sound_off;
                playerCharacter.getMovesound().Stop();
            }
            else
            {
                btnToggleSound.BackgroundImage = global::Piirto.Properties.Resources.sound_on;
                if (currentMode == MODE.REDRAW)
                {
                    playerCharacter.getMovesound().PlayLooping();
                }
            }
        }

        private void btnChangeSprite_Click(object sender, EventArgs e)
        {
            playerCharacter.getMovesound().Stop();
            playerCharacter.getCrashsound().Stop();
            playerCharacter.getWinsound().Stop();
            selectSpriteDlg.ShowDialog();

            Character oldCharacter = playerCharacter;
            playerCharacter = selectSpriteDlg.getSelected();
            if (playerCharacter != oldCharacter)
            {
                playerCharacter.getSprite().setLocation(oldCharacter.getSprite().getLocation());
                playerCharacter.getSprite().setAngle(oldCharacter.getSprite().getAngle());
                playerCharacter.getSprite().Visible = oldCharacter.getSprite().Visible;
                playerCharacter.getSprite().Snapped = false;

                oldCharacter.getSprite().Visible = false;
                oldCharacter.getSprite().Snapped = false;
                oldCharacter.getMovesound().Stop();
                oldCharacter.getCrashsound().Stop();
                oldCharacter.getWinsound().Stop();
                setPenWidth(penWidth);
            }
            if (currentMode == MODE.REDRAW && isSoundOn)
            {
                playerCharacter.getMovesound().PlayLooping();
            }

            draw();
        }
    }
}
